/*
 * operators.c
 *
 * Copyright (C) 2015 - kali
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#define LEXER_H
#include "include/types.h"
#include "include/parse.h"
#include "include/lexer.h"
#include "include/axioms.h"

static atom *evallist(acons*,nspace*);

atom *lookup(nspace *search, asym *name) {
	//printf("starting lookup of %s\n", name->s);
	if (!search)
	{
		printf("lookup of %s failed\n", name->s);
		exit(1);
		return NULL;
	}
	if (lmd_eq(lmd_catom(name), lmd_catom(search->name)))
		return search->link;
	return lookup(search->next, name);
}



atom *contperm;
/*
atom *icont(acont *c, atom *ret) {
	//printf("icont called\n");
	static int size;
	static char *from, *to;
	
	char tos;
	
	if (c->respt < &tos)
		return icont(c, ret);
	
	contperm = ret;
	size = c->size;
	from = c->stack;
	to = c->respt;
	
	while (size--)
		*(to++) = *(from++);

	longjmp(c->registers, 0);
}
*/
atom *apply (afun *fn, acons *args, nspace *n) {	
	nspace *x = fn->n;
	if (args){
		if (*(fn->args) == TSYM)
			x = lmd_bind(x, lmd_csym(fn->args), evallist(args, n));
		else
		{
			atom *fargs = fn->args;
			while (fargs && *fargs == TCONS && args)
			{
				x = lmd_bind(x, lmd_csym(lmd_ccons(fargs)->car), eval(args->car, n));
				args = lmd_ccons(args->cdr);
				fargs = lmd_ccons(fargs)->cdr;
			}
			if (fargs && *fargs == TCONS)
				printf("too few arguments\n");
			else
				if (fargs)
					x = lmd_bind(x, lmd_csym(fargs), evallist(args, n));
				else
					if (args)
						printf("too many arguments\n");
		}
	}
	
	if (*lmd_catom(fn) == TFUN)
		return begin(fn->body, x);
	return begin(fn->body, n);
}

/**
	eval will act only as a macroexpander, function calling will be left
	up to the individual operation. how will this work?
**/
atom *eval (atom *expr, nspace *n) {
	//char tos;
	//printf("eval tos: %i\n", &tos);
	
	//if (debug) {
	//	printf("eval ["); print(expr); printf("]\n");
	//}
	
	if (!expr) return expr;
	
	acons *c;
	atom *o;
	
	//printf("eval A\n");
	
	switch (*expr) {
		case TCONS:
			//printf("eval B\n");
			c = (acons *) expr;
			o = eval(c->car, n);
			if (!o)
			{
				printf("invalid procedure: null\n");
				return NULL;
			}
		//	printf("eval C\n");
			//atom *xyz;
			switch (*o)
			{
				case TFUN: 
				case TMAC:
					//printf("eval D\n");
					return apply((afun *) o, lmd_ccons(c->cdr), n);
				case TAX:
					//printf("eval E\n");
					return (lmd_cax(o)->primitive)(lmd_ccons(c->cdr), n);
					/*
				case TCONT:
					//printf("eval F\n");
					if (!c->cdr)
					{
						printf("continuation requires an argument to return\n");
						exit(0);
					}
					xyz = eval(ccons(c->cdr)->car, n);
					//printf("eval J\n");
					return icont((acont *) o, xyz);
					*/
				default:
					printf("#leavesbnw#");
					break;
			}
			break;
		case TSYM:
			//printf("eval G\n");
			return lookup(n, (asym *) expr);
		default:
			//printf("eval H\n");
			return expr;
	}
	//printf("eval I\n");
	return 0;//maybe wrong
}

static atom *evallist (acons *list, nspace *n) {
	if (!list)
		return NULL;
	return lmd_newcons(eval(list->car, n), evallist(lmd_ccons(list->cdr), n));
}

void loadfile(char *name, nspace *n) {
	
	FILE *file = fopen(name, "r");
	int c;
	
	for (;;) {
		while ((c = fgetc(file)) != EOF && isspace(c));
		if (c == EOF) break;
		ungetc(c, file);
		eval(lmd_read(file), n);
	}
	
	fclose(file);
}
